/*
 * %W% %E%
 *
 * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package com.sun.corba.se.impl.presentation.rmi ;

import java.lang.reflect.InvocationHandler ;
import java.lang.reflect.Proxy ;
import java.lang.reflect.Method ;

import org.omg.CORBA.portable.ObjectImpl ;

import java.io.ObjectStreamException ;
import java.io.Serializable ;

import com.sun.corba.se.spi.presentation.rmi.IDLNameTranslator ;
import com.sun.corba.se.spi.presentation.rmi.PresentationManager ;
import com.sun.corba.se.spi.presentation.rmi.DynamicStub ;

import com.sun.corba.se.spi.orbutil.proxy.LinkedInvocationHandler ;
import com.sun.corba.se.spi.orbutil.proxy.InvocationHandlerFactory ;
import com.sun.corba.se.spi.orbutil.proxy.DelegateInvocationHandlerImpl ;
import com.sun.corba.se.spi.orbutil.proxy.CompositeInvocationHandler ;
import com.sun.corba.se.spi.orbutil.proxy.CompositeInvocationHandlerImpl ;

public class InvocationHandlerFactoryImpl implements InvocationHandlerFactory 
{
    private final PresentationManager.ClassData classData ;
    private final PresentationManager pm ;
    private Class[] proxyInterfaces ;

    public InvocationHandlerFactoryImpl( PresentationManager pm,
	PresentationManager.ClassData classData ) 
    {
	this.classData = classData ;
	this.pm = pm ;

    	Class[] remoteInterfaces = 
	    classData.getIDLNameTranslator().getInterfaces() ;
	proxyInterfaces = new Class[ remoteInterfaces.length + 1 ] ;
	for (int ctr=0; ctr<remoteInterfaces.length; ctr++)
	    proxyInterfaces[ctr] = remoteInterfaces[ctr] ;

	proxyInterfaces[remoteInterfaces.length] = DynamicStub.class ;
    }

    private class CustomCompositeInvocationHandlerImpl extends
	CompositeInvocationHandlerImpl implements LinkedInvocationHandler, 
	Serializable
    {
	private transient DynamicStub stub ;

	public void setProxy( Proxy proxy ) 
	{
	    ((DynamicStubImpl)stub).setSelf( (DynamicStub)proxy ) ;
	}

	public Proxy getProxy()
	{
	    return (Proxy)((DynamicStubImpl)stub).getSelf() ;
	}

	public CustomCompositeInvocationHandlerImpl( DynamicStub stub )
	{
	    this.stub = stub ;
	}

	/** Return the stub, which will actually be written to the stream.
	 * It will be custom marshalled, with the actual writing done in
	 * StubIORImpl.  There is a corresponding readResolve method on
	 * DynamicStubImpl which will re-create the full invocation
	 * handler on read, and return the invocation handler on the 
	 * readResolve method.
	 */
	public Object writeReplace() throws ObjectStreamException
	{
	    return stub ;
	}
    }

    public InvocationHandler getInvocationHandler() 
    {
	final DynamicStub stub = new DynamicStubImpl( 
	    classData.getTypeIds() ) ; 

	return getInvocationHandler( stub ) ;
    }

    // This is also used in DynamicStubImpl to implement readResolve.
    InvocationHandler getInvocationHandler( DynamicStub stub ) 
    {
	// Create an invocation handler for the methods defined on DynamicStub,
	// which extends org.omg.CORBA.Object.  This handler delegates all
	// calls directly to a DynamicStubImpl, which extends 
	// org.omg.CORBA.portable.ObjectImpl.
	InvocationHandler dynamicStubHandler = 
	    DelegateInvocationHandlerImpl.create( stub ) ;

	// Create an invocation handler that handles any remote interface
	// methods.
	InvocationHandler stubMethodHandler = new StubInvocationHandlerImpl( 
	    pm, classData, stub ) ;

	// Create a composite handler that handles the DynamicStub interface
	// as well as the remote interfaces.
	final CompositeInvocationHandler handler = 
	    new CustomCompositeInvocationHandlerImpl( stub ) ;
	handler.addInvocationHandler( DynamicStub.class,
	    dynamicStubHandler ) ;
	handler.addInvocationHandler( org.omg.CORBA.Object.class,
	    dynamicStubHandler ) ;
	handler.addInvocationHandler( Object.class,
	    dynamicStubHandler ) ;

	// If the method passed to invoke is not from DynamicStub or its superclasses,
	// it must be from an implemented interface, so we just handle
	// all of these with the stubMethodHandler.  This used to be
	// done be adding explicit entries for stubMethodHandler for 
	// each remote interface, but that does not work correctly
	// for abstract interfaces, since the graph analysis ignores
	// abstract interfaces in order to compute the type ids 
	// correctly (see PresentationManagerImpl.NodeImpl.getChildren).
	// Rather than produce more graph traversal code to handle this
	// problem, we simply use a default.
	// This also points to a possible optimization: just use explict
	// checks for the three special classes, rather than a general
	// table lookup that usually fails.
	handler.setDefaultHandler( stubMethodHandler ) ;

	return handler ;
    }

    public Class[] getProxyInterfaces()
    {
	return proxyInterfaces ;
    }
}
